﻿using System.Text.Json;
using WeatherSpider.Models;
using HtmlAgilityPack;

namespace WeatherSpider.Spider
{
    public class CmaWeatherSpider
    {
        private static readonly Dictionary<string, string> provinceCode = new()
        {
            { "北京", "ABJ" }, { "河北", "AHE" }, { "内蒙古", "ANM" },
            { "山西", "ASX" }, { "天津", "ATJ" }, { "黑龙江", "AHL" },
            { "吉林", "AJL" }, { "辽宁", "ALN" }, { "安徽", "AAH" },
            { "福建", "AFJ" }, { "江西", "AJX" }, { "江苏", "AJS" },
            { "山东", "ASD" }, { "上海", "ASH" }, { "浙江", "AZJ" },
            { "湖南", "AHN" }, { "湖北", "AHB" }, { "河南", "AHA" },
            { "广东", "AGD" }, { "广西", "AGX" }, { "海南", "AHI" },
            { "甘肃", "AGS" }, { "宁夏", "ANX" }, { "青海", "AQH" },
            { "陕西", "ASN" }, { "新疆", "AXJ" }, { "重庆", "ACQ" },
            { "贵州", "AGZ" }, { "四川", "ASC" }, { "西藏", "AXZ" },
            { "云南", "AYN" }, { "澳门", "AAM" }, { "香港", "AXG" },
            { "台湾", "ATW" }
        };
        private static readonly HttpClient client;

        static CmaWeatherSpider()
        {
            client = new HttpClient();
            client.BaseAddress = new Uri("https://weather.cma.cn/web/weather/");
        }

        /// <summary>
        /// 爬取国内城市列表
        /// </summary>
        /// <returns></returns>
        /// <exception cref="HttpRequestException"></exception>
        public static async Task<IEnumerable<Province>> GetCityInfo()
        {
            var client = new HttpClient { BaseAddress = new Uri("https://weather.cma.cn/api/dict/province/") };
            var tasks = new List<Task<Province>>(provinceCode.Count);
            foreach (var province in provinceCode)
            {
                var task = Task.Run(async () =>
                {
                    var result = new Province() { Name = province.Key, Code = province.Value };
                    var response = await client.GetAsync(province.Value).ConfigureAwait(false);
                    var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
                    var apiResult = JsonDocument.Parse(content);
                    var data = apiResult.RootElement.GetProperty("data").GetString() ?? throw new HttpRequestException();
                    var cities = data.Split('|');
                    var citiList = new List<City>(cities.Length);
                    foreach (var city in cities)
                    {
                        var cityInfo = city.Split(',');
                        citiList.Add(new City() { Name = cityInfo[1], Code = cityInfo[0] });
                    }
                    result.Cities = citiList;
                    Console.WriteLine($"已获取{province.Key}的城市列表");
                    return result;
                });
                tasks.Add(task);
            }

            await Task.WhenAll(tasks).ConfigureAwait(false);

            return tasks.Select(task => task.Result);
        }

        /// <summary>
        /// 爬取指定城市的一周天气
        /// </summary>
        /// <param name="city"></param>
        /// <returns></returns>
        public static async Task<IEnumerable<OnedayWeather>> GetWeekWeather(City city)
        {
            var response = await client.GetAsync(city.Code).ConfigureAwait(false);

            response.EnsureSuccessStatusCode();

            var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);

            var document = new HtmlDocument();
            document.LoadHtml(content);

            var dayList = document.GetElementbyId("dayList") ?? throw new Exception("找不到dayList");

            // 从dayList中解析出一周的天气
            return from day in dayList.ChildNodes.Where(n => n.NodeType == HtmlNodeType.Element)
                   let dayItems = day.ChildNodes.Where(n => n.NodeType == HtmlNodeType.Element).ToArray()
                   let date = dayItems[0].ChildNodes.Last(n => n.NodeType == HtmlNodeType.Text).InnerText
                   let temperatures = dayItems[5].ChildNodes.Single(n => n.NodeType == HtmlNodeType.Element)
                                                 .ChildNodes.Where(n => n.NodeType == HtmlNodeType.Element)
                                                 .Select(n => Convert.ToInt32(n.InnerText.TrimEnd('℃')))
                   select new OnedayWeather
                   {
                       Date = DateOnly.Parse(date),
                       Day = new()
                       {
                            Description = dayItems[2].InnerText,
                            WindDirection = dayItems[3].InnerText,
                            WindScale = dayItems[4].InnerText,
                            Temperature = temperatures.First()
                       },
                       Night = new()
                       {
                            Description = dayItems[7].InnerText,
                            WindDirection = dayItems[8].InnerText,
                            WindScale = dayItems[9].InnerText,
                            Temperature = temperatures.Last()
                       }
                   };
        }
    }
}
